import pygame
import random
#游戏窗口区域(矩形区域)
SCREEN_RECT=pygame.Rect(0,0,480,700)
FRAME_INTERVAL=10
HERO_BOMB_COUNT=3
#英雄飞机默认初始位置
HERO_DEFAULT_MID_BOTTOM=(SCREEN_RECT.centerx,SCREEN_RECT.bottom-90)
HERO_DEAD_EVENT=pygame.USEREVENT
HERO_POWER_OFF_EVENT=pygame.USEREVENT+1
HERO_FIRE_EVENT=pygame.USEREVENT+2
THROW_SUPPLY_EVENT=pygame.USEREVENT+3
BULLET_ENHANCED_OFF_EVENT=pygame.USEREVENT+4

class GameSprite(pygame.sprite.Sprite):
    """游戏精灵类"""
    res_path="./res/images/"
    def __init__(self,image_name,speed,*groups):
        """构造方法
        :param image_name:要加载的图片文件名
        :param speed:移动速度,0 表示静止
        :param groups:要添加到的精灵组，不传值则不添加
        """
        super().__init__(*groups)
        #图像
        self.image =pygame.image.load(self.res_path + image_name)
        self.rect =self.image.get_rect()
        self.speed=speed
        #图像遮罩，可以提高碰撞检测的执行性能
        self.mask=pygame.mask.from_surface(self.image)
    def update(self,*args):
        """更新精灵位置,默认再垂直方向移动"""
        self.rect.y +=self.speed

class Background(GameSprite):
    """背景精灵类"""
    def __init__(self,is_alt,speed,*groups):
        #调用父类方法实现精灵的创建
        super().__init__("background.png",1,*groups)
        #判断是否为有、另一个精灵
        if is_alt:
            self.rect.y = -self.rect.h#设置到游戏窗口正上方
    def update(self, *args):
        #父类向下运动
        super().update(*args)#向下运动
        #判断是否出屏幕，如是则设置到屏幕上方
        if self.rect.y >=self.rect.h:
            self.rect.y = -self.rect.h

class StatusButton(GameSprite):
    """状态按钮类"""
    def __init__(self, image_names,*groups):
        """构造方法
        :param image_names:要加载的图像名称列表
        :param groups:要添加到的精灵组
        """
        super().__init__(image_names[0], 0, *groups)
        #加载图像
        self.images=[pygame.image.load(self.res_path +name)
                        for name in image_names]
    def switch_status(self,is_pause):
        """切换状态
        :param is_pause:是否暂停要加载的图像名称列表
        """
        self.image=self.images[1 if is_pause else 0]

class Label(pygame.sprite.Sprite):
    """文本标签精灵"""
    font_path="./res/font/MarkerFelt.ttc"#字体文件路径
    def __init__(self,text,size,color,*groups):
        """构造方法
        :param text:文本内容
        :param size:字体大小
        :param color:字体颜色
        :param groups:要添加到的精灵组
        """
        super().__init__(*groups)
        self.font=pygame.font.Font(self.font_path,size)
        self.color=color
        self.image =self.font.render(text,True,self.color)
        self.rect=self.image.get_rect()
    def set_text(self,text):
        """设置文本,使用指定的文本重新渲染image,并且更新rect
        :param text:文本内容
        """
        self.image=self.font.render(text,True,self.color)
        self.rect=self.image.get_rect()

class Plane(GameSprite):
    """飞机类"""
    def __init__(self,hp, speed,value,wav_name,normal_names,hurt_name,destroy_names,*groups):
        """构造方法
        :param normal_names:记录正常飞行状态的图像名称列表
        :param groups:要添加到精灵组
        """
        super().__init__(normal_names[0],speed,*groups)
        #飞机属性
        self.hp=hp
        self.max_hp=hp
        self.value=value
        self.wav_name=wav_name
        #加载图像列表
        self.normal_images=[pygame.image.load(self.res_path+name)
                            for name in normal_names]
        self.normal_index=0
        #受伤图片
        self.hurt_name=pygame.image.load(self.res_path+hurt_name)
        #被摧毁图像列表及索引
        self.destroy_images=[pygame.image.load(self.res_path+name)
                            for name in destroy_names]
        self.destroy_index=0

    def reset_plane(self):
        """重置飞机"""
        self.hp=self.max_hp
        self.normal_index=0
        self.destroy_index=0
        self.image=self.normal_images[0]
        self.speed=random.randint(1,self.max_speed)

    def update(self, *args):
        """更新图像和位置"""
        #调用父类方法更新敌机图像-注意args需要拆包
        super().update(*args)
        #判断敌机是否被摧毁，若没有，则根据速度更新敌机的位置
        if self.hp>0:
            self.rect.y +=self.speed
            #判断是否飞出屏幕，如果是，重置敌机
            if self.rect.y>=SCREEN_RECT.h:
                self.reset_plane()
        # #如果第一个参数为False，不需要更新图像，直接返回
        # if not args[0]:
        #     return
        # #判断飞机状态
        # if self.hp==self.max_hp:
        #     self.image=self.normal_images[self.normal_index]
        #     count=len(self.normal_images)
        #     self.normal_index=(self.normal_index+1)%count
        # elif self.hp>0:
        #     self.image=self.hurt_name
        # else:
        #     #判断是否显示到最后一张图片，若是说明飞机完全被摧毁
        #     if self.destroy_index<len(self.destroy_images):
        #         self.image=self.destroy_images[self.destroy_index]
        #         self.destroy_index+=1
        #     else:
        #         self.reset_plane()
        # #设置图像
        # self.image=self.normal_images[self.normal_index]
        # #更新索引
        # count=len(self.normal_images)
        # self.normal_index=(self.normal_index+1)%count
class Enemy(Plane):
    """敌机类"""
    def __init__(self,kind,max_speed ,*groups):
        #记录敌机类型和最大速度
        self.kind=kind
        self.max_speed=max_speed
        #根据类型调用父类方法传递不同参数
        if kind ==0:
            super().__init__(1,1,1000,"enemy1_down.wav",
                            ["enemy1.png"],"enemy1.png",
                            ["enemy1_down%d.png"% i for i in range(1,5)],*groups)
        elif kind==1:
            super().__init__(6,1,6000,"enemy2_down.wav",
                            ["enemy2.png"],"enemy2_hit.png",
                            ["enemy2_down%d.png"% i for i in range(1,5)],*groups)
        else:
            super().__init__(15,1,15000,"enemy3_down.wav",
                            ["enemy3_n1.png","enemy3_n2.png"],
                            "enemy3_hit.png",
                            ["enemy3_down%d.png"% i for i in range(1,7)],*groups)
        #调用重置飞机的方法，设置敌机初始位置和速度
        self.reset_plane()
    def reset_plane(self):
        """重置飞机"""
        super().reset_plane()
        #设置随机x值
        x=random.randint(0,SCREEN_RECT.w-self.rect.w)
        #设置随机y值
        y=random.randint(0,SCREEN_RECT.h-self.rect.h)-SCREEN_RECT.h
        self.rect.topleft=(x,y)
        #设置初始随机位置和速度
        pass

class Hero(Plane):
    """英雄飞机"""
    def __init__(self,*groups):
        """构造方法
        :param groups:要添加到的精灵组
        """
        super().__init__(1000,0,5,"me_down.wav",
                        ["me%d.png"% i for i in range(1,3)],"me1.png",
                        ["me_destroy_%d.png"% i for i in range(1,5)],*groups)
        self.is_power=False
        self.bomb_count=HERO_BOMB_COUNT
        self.bullets_kind=0
        self.bullets_group=pygame.sprite.Group()
        #初始位置
        self.rect.midbottom=HERO_DEFAULT_MID_BOTTOM
        self.bullets_kind=0
        self.bullets_group=pygame.sprite.Group()
        #初始位置
        self.rect.midbottom=HERO_DEFAULT_MID_BOTTOM
        #设置0.2s发射子弹定时器事件
        pygame.time.set_timer(HERO_FIRE_EVENT,200)
    
    def update(self, *args):
        """更新英雄飞机的图像及矩形区域
        :param args:0 更新图像标记,1 水平移动基数,2 垂直移动基数
        """
        #调用父类方法更新飞机图像 -注意 args 需要解包
        super().update(*args)
        #如果没有传递方向基数或者英雄飞机被摧毁，直接返回
        if len(args) != 3 or self.hp <=0:
            return
        #调整水平移动距离
        self.rect.x +=args[1]*self.speed
        self.rect.y +=args[2]*self.speed
        #限定在游戏窗口内部移动
        self.rect.x=0 if self.rect.x<0 else self.rect.x
        if self.rect.right>SCREEN_RECT.right:
            self.rect.right=SCREEN_RECT.right
        self.rect.y=0 if self.rect.y<0 else self.rect.y
        if self.rect.bottom>SCREEN_RECT.bottom:
            self.rect.bottom=SCREEN_RECT.bottom

    def blowup(self,enemies_group):
        """引爆炸弹
        :param enemies_group:敌机精灵组
        :return:累计得分
        """
        #如果没有足够数量的炸弹或者英雄飞机被摧毁，直接返回
        if self.bomb_count<=0 or self.hp<-0:
            return 0
        self.bomb_count-=1
        score=0
        count=0
        #遍历敌机精灵组，将游戏窗口内的敌机引爆
        for enemy in enemies_group.sprites():
            #判断敌机是否进入游戏窗口
            if enemies_group>0:
                score +=enemy.value
                count+=1
                enemy.hp=0
        print("炸毁了%d架敌机,得分%d"%(count,score))
        return score
    def reset_plane(self):
        """重置英雄飞机"""
        #调用父类方法重置图像相关属性
        super().reset_plane()
        self.is_power=True
        self.bomb_count=HERO_BOMB_COUNT
        self.bullets_kind=0
        #发布英雄牺牲事件
        pygame.event.post(pygame.event.Event(HERO_DEAD_EVENT))
        #3秒定时器
        pygame.time.set_timer(HERO_DEAD_EVENT,3000)

    def fire(self,display_group):
        """发射子弹
        :param display_group:要添加到显示精灵组
        """
        #需要将子弹精灵添加到2个精灵组
        groups=(self.bullets_group,display_group)
        #测试子弹增强效果
        # self.bullets_kind=1
        for i in range(3):
            #创建子弹精灵
            bullet1=Bullet(self.bullets_kind,*groups)
            #计算子弹的垂直位置
            y=self.rect.y -i *15
            #判断子弹类型
            if self.bullets_kind==0:
                bullet1.rect.midbottom=(self.rect.centerx,y)
            else:
                bullet1.rect.midbottom=(self.rect.centerx-20,y)
                #再创建一颗子弹
                bullet2=Bullet(self.bullets_kind,*groups)
                bullet2.rect.midbottom=(self.rect.centerx+20,y)

class Bullet(GameSprite):
    """子弹类"""
    def __init__(self,kind ,*groups):
        """构造方法
        :param kind:子弹类型
        :param groups:要添加到精灵组
        """
        image_name="bullet1.png" if kind==0 else"bullet2.png"
        super().__init__(image_name, -12,*groups)
        self.damage=1
    def update(self, *args):
        super().update(*args)
        #判读是否从上发飞出窗口
        if self.rect.bottom<0:
            self.kill()
    
class Supply(GameSprite):
    """道具类"""
    def __init__(self,kind, *groups):
        #调用父类
        image_name="%s_supply.png"% ("bomb" if kind ==0 else "bullet")
        super().__init__(image_name, 5, *groups)
        #道具类型
        self.kind=kind
        #音频文件
        self.wav_name="get_%s.wav"%("bomb" if kind ==0 else "bullet")
        #初始位置
        self.rect.y=SCREEN_RECT.h
    def throw_supply(self):
        """投放道具"""
        self.rect.bottom=0
        self.rect.x=random.randint(0,SCREEN_RECT.w-self.rect.w)
    def update(self, *args):
        """更新位置，再屏幕下方不移动"""
        if self.rect.h>SCREEN_RECT.h:
            return
        #调用父类，沿垂直方向移动
        super().update(*args)
        